#include <buola/gui/x11/auto.h>

#include <buola/app/ceventloop.h>
#include <buola/gui/cwindow.h>
#include <buola/image/cimage.h>
#include <buola/gui/x11/cpixmap.h>
#include <buola/gui/x11/cx11color.h>
#include <buola/gui/x11/cx11clipboard.h>
#include <buola/gui/x11/cx11toolkit.h>
#include <buola/gui/x11/cx11display.h>
#include <buola/gui/x11/cx11drawable.h>
#include <buola/gui/x11/cx11window.h>
#include <buola/gui/x11/cx11windowwm.h>
#include <buola/gui/x11/cx11dispatcher.h>
#include <buola/gui/x11/cx11session.h>
#include <buola/gui/x11/cx11picture.h>
#include <buola/gui/x11/cglxinterface.h>
#include <buola/gui/x11/cx11exposehelper.h>
#include <buola/gui/x11/cglxexposehelper.h>
#include <buola/gui/x11/cglxcontext.h>

//#define SN_API_NOT_YET_FROZEN
//#include <libsn/sn.h>

#include <X11/keysym.h>
#include <X11/XF86keysym.h>
#include <X11/cursorfont.h>

//TODO:Parse some general command-line options

namespace buola { namespace gui { namespace x11 {

bool CX11Toolkit::mSMDisabled=false;
KeySym CX11Toolkit::mKeyToKeysym[KEY_COUNT];
bool CX11Toolkit::mKeysInit=false;

CX11Toolkit::CX11Toolkit()
    :   mRootWindow(nullptr)
    ,   mDispatcher(nullptr),
        mSession(nullptr),
        mNETSupportedInit(false)
{}

CX11Toolkit::~CX11Toolkit()
{
    Finish();
}

CX11Window *CX11Toolkit::GetRootWindow()
{
    if(!mRootWindow) mRootWindow=new CX11Window((Window)0,mDisplay);
    return mRootWindow;
}

CX11Window *CX11Toolkit::GetNewProxyWindow()
{
    CX11Window *lWindow=new CX11Window(nullptr,mDisplay);
    ///\todo properly set events and attribs of window, especially no_managing
    lWindow->Create(nullptr,CPoint_d(0,0),CSize_d(1,1));

    return lWindow;
}

void CX11Toolkit::InitKeys()
{
    mKeyToKeysym[KEY_NONE]=0;
    mKeyToKeysym[KEY_ESC]=XK_Escape;
    mKeyToKeysym[KEY_0]=XK_0;
    mKeyToKeysym[KEY_1]=XK_1;
    mKeyToKeysym[KEY_2]=XK_2;
    mKeyToKeysym[KEY_3]=XK_3;
    mKeyToKeysym[KEY_4]=XK_4;
    mKeyToKeysym[KEY_5]=XK_5;
    mKeyToKeysym[KEY_6]=XK_6;
    mKeyToKeysym[KEY_7]=XK_7;
    mKeyToKeysym[KEY_8]=XK_8;
    mKeyToKeysym[KEY_9]=XK_9;
    mKeyToKeysym[KEY_MINUS]=XK_minus;
    mKeyToKeysym[KEY_EQUAL]=XK_equal;

    mKeyToKeysym[KEY_BACKSPACE]=XK_BackSpace;
    mKeyToKeysym[KEY_TAB]=XK_Tab;

    mKeyToKeysym[KEY_Q]=XK_q;
    mKeyToKeysym[KEY_W]=XK_w;
    mKeyToKeysym[KEY_E]=XK_e;
    mKeyToKeysym[KEY_R]=XK_r;
    mKeyToKeysym[KEY_T]=XK_t;
    mKeyToKeysym[KEY_Y]=XK_y;
    mKeyToKeysym[KEY_U]=XK_u;
    mKeyToKeysym[KEY_I]=XK_i;
    mKeyToKeysym[KEY_O]=XK_o;
    mKeyToKeysym[KEY_P]=XK_p;
    mKeyToKeysym[KEY_LBRACKET]=XK_bracketleft;
    mKeyToKeysym[KEY_RBRACKET]=XK_bracketright;

    mKeyToKeysym[KEY_ENTER]=XK_Return;

    mKeyToKeysym[KEY_LCONTROL]=XK_Control_L;

    mKeyToKeysym[KEY_A]=XK_a;
    mKeyToKeysym[KEY_S]=XK_s;
    mKeyToKeysym[KEY_D]=XK_d;
    mKeyToKeysym[KEY_F]=XK_f;
    mKeyToKeysym[KEY_G]=XK_g;
    mKeyToKeysym[KEY_H]=XK_h;
    mKeyToKeysym[KEY_J]=XK_j;
    mKeyToKeysym[KEY_K]=XK_k;
    mKeyToKeysym[KEY_L]=XK_l;
    mKeyToKeysym[KEY_SEMICOLON]=XK_semicolon;
    mKeyToKeysym[KEY_APOSTROPHE]=XK_apostrophe;
    mKeyToKeysym[KEY_GRAVE]=XK_grave;

    mKeyToKeysym[KEY_LSHIFT]=XK_Shift_L;
    mKeyToKeysym[KEY_BACKSLASH]=XK_backslash;

    mKeyToKeysym[KEY_Z]=XK_z;
    mKeyToKeysym[KEY_X]=XK_x;
    mKeyToKeysym[KEY_C]=XK_c;
    mKeyToKeysym[KEY_V]=XK_v;
    mKeyToKeysym[KEY_B]=XK_b;
    mKeyToKeysym[KEY_N]=XK_n;
    mKeyToKeysym[KEY_M]=XK_m;
    mKeyToKeysym[KEY_COMMA]=XK_comma;
    mKeyToKeysym[KEY_PERIOD]=XK_period;
    mKeyToKeysym[KEY_SLASH]=XK_slash;

    mKeyToKeysym[KEY_RSHIFT]=XK_Shift_R;
    mKeyToKeysym[KEY_KPMULTIPLY]=XK_KP_Multiply;

    mKeyToKeysym[KEY_LALT]=XK_Alt_L;
    mKeyToKeysym[KEY_SPACE]=XK_space;
    mKeyToKeysym[KEY_CAPSLOCK]=XK_Caps_Lock;

    mKeyToKeysym[KEY_F1]=XK_F1;
    mKeyToKeysym[KEY_F2]=XK_F2;
    mKeyToKeysym[KEY_F3]=XK_F3;
    mKeyToKeysym[KEY_F4]=XK_F4;
    mKeyToKeysym[KEY_F5]=XK_F5;
    mKeyToKeysym[KEY_F6]=XK_F6;
    mKeyToKeysym[KEY_F7]=XK_F7;
    mKeyToKeysym[KEY_F8]=XK_F8;
    mKeyToKeysym[KEY_F9]=XK_F9;
    mKeyToKeysym[KEY_F10]=XK_F10;

    mKeyToKeysym[KEY_NUMLOCK]=XK_Num_Lock;
    mKeyToKeysym[KEY_SCROLLLOCK]=XK_Scroll_Lock;

    mKeyToKeysym[KEY_KP7]=XK_KP_7;
    mKeyToKeysym[KEY_KPHOME]=XK_KP_Home;
    mKeyToKeysym[KEY_KP8]=XK_KP_8;
    mKeyToKeysym[KEY_KPUP]=XK_KP_Up;
    mKeyToKeysym[KEY_KP9]=XK_KP_9;
    mKeyToKeysym[KEY_KPPGUP]=XK_KP_Page_Up;
    mKeyToKeysym[KEY_KPMINUS]=XK_KP_Subtract;
    mKeyToKeysym[KEY_KP4]=XK_KP_4;
    mKeyToKeysym[KEY_KPLEFT]=XK_KP_Left;
    mKeyToKeysym[KEY_KP5]=XK_KP_5;
    mKeyToKeysym[KEY_KP6]=XK_KP_6;
    mKeyToKeysym[KEY_KPRIGHT]=XK_KP_Right;
    mKeyToKeysym[KEY_KPPLUS]=XK_KP_Add;
    mKeyToKeysym[KEY_KP1]=XK_KP_1;
    mKeyToKeysym[KEY_KPEND]=XK_KP_End;
    mKeyToKeysym[KEY_KP2]=XK_KP_2;
    mKeyToKeysym[KEY_KPDOWN]=XK_KP_Down;
    mKeyToKeysym[KEY_KP3]=XK_KP_3;
    mKeyToKeysym[KEY_KPPGDN]=XK_KP_Page_Down;
    mKeyToKeysym[KEY_KP0]=XK_KP_0;
    mKeyToKeysym[KEY_KPINS]=XK_KP_Insert;
    mKeyToKeysym[KEY_KPPERIOD]=XK_KP_Decimal;
    mKeyToKeysym[KEY_KPDEL]=XK_KP_Delete;
    mKeyToKeysym[KEY_KPEQUAL]=XK_KP_Equal;

    mKeyToKeysym[KEY_LESS]=XK_less;

    mKeyToKeysym[KEY_F11]=XK_F11;
    mKeyToKeysym[KEY_F12]=XK_F12;
    mKeyToKeysym[KEY_F13]=XK_F13;
    mKeyToKeysym[KEY_F14]=XK_F14;
    mKeyToKeysym[KEY_F15]=XK_F15;
    mKeyToKeysym[KEY_F16]=XK_F16;
    mKeyToKeysym[KEY_F17]=XK_F17;
    mKeyToKeysym[KEY_F18]=XK_F18;
    mKeyToKeysym[KEY_F19]=XK_F19;

    mKeyToKeysym[KEY_KPENTER]=XK_KP_Enter;
    mKeyToKeysym[KEY_RCONTROL]=XK_Control_R;
    mKeyToKeysym[KEY_KPDIVIDE]=XK_KP_Divide;
    mKeyToKeysym[KEY_PRINTSCREEN]=XK_Print;
    mKeyToKeysym[KEY_ALTGR]=XK_Mode_switch;
    mKeyToKeysym[KEY_PAUSE]=XK_Pause;
    mKeyToKeysym[KEY_BREAK]=XK_Break;

    mKeyToKeysym[KEY_HOME]=XK_Home;
    mKeyToKeysym[KEY_UP]=XK_Up;
    mKeyToKeysym[KEY_PGUP]=XK_Page_Up;
    mKeyToKeysym[KEY_LEFT]=XK_Left;
    mKeyToKeysym[KEY_RIGHT]=XK_Right;
    mKeyToKeysym[KEY_END]=XK_End;
    mKeyToKeysym[KEY_DOWN]=XK_Down;
    mKeyToKeysym[KEY_PGDN]=XK_Page_Down;
    mKeyToKeysym[KEY_INS]=XK_Insert;
    mKeyToKeysym[KEY_DEL]=XK_Delete;
    mKeyToKeysym[KEY_LWIN]=XK_Super_L;
    mKeyToKeysym[KEY_RWIN]=XK_Multi_key;
    mKeyToKeysym[KEY_WINMENU]=XK_Menu;
    mKeyToKeysym[KEY_COMMAND]=XK_Hyper_L;
    mKeyToKeysym[KEY_FUNCTION]=XK_Meta_L;
    mKeyToKeysym[KEY_HELP]=XK_Help;
    
    mKeyToKeysym[KEY_VOLUMEUP]=XF86XK_AudioRaiseVolume;
    mKeyToKeysym[KEY_VOLUMEDOWN]=XF86XK_AudioLowerVolume;
    mKeyToKeysym[KEY_AUDIOMUTE]=XF86XK_AudioMute;
}

EKeyMod CX11Toolkit::GetMods(unsigned int pMods)
{
    EKeyMod lRet=KEYMOD_NONE;

    if(pMods&ShiftMask)         lRet|=KEYMOD_SHIFT;
    if(pMods&ControlMask)       lRet|=KEYMOD_CONTROL;
    if(pMods&Mod1Mask)          lRet|=KEYMOD_ALT;
    if(pMods&0x2000)            lRet|=KEYMOD_ALTGR;
    if(pMods&Mod4Mask)          lRet|=KEYMOD_WIN;

    return lRet;
}

EKeyLock CX11Toolkit::GetLocks(unsigned int pMods)
{
    EKeyLock lRet=KEYLOCK_NONE;

    if(pMods&Mod2Mask)          lRet|=KEYLOCK_NUM;
    if(pMods&LockMask)          lRet|=KEYLOCK_CAPS;
    if(pMods&Mod5Mask)          lRet|=KEYLOCK_SCROLL;

    return lRet;
}

EMouseButton CX11Toolkit::GetButtons(unsigned int pMods)
{
    EMouseButton lRet=EMouseButton::NONE;

    if(pMods&Button1Mask)          lRet|=EMouseButton::LEFT;
    if(pMods&Button2Mask)          lRet|=EMouseButton::RIGHT;
    if(pMods&Button3Mask)          lRet|=EMouseButton::MIDDLE;

    return lRet;
}

static const unsigned int sAllXLockCombs[8]=
{
    0,
    Mod2Mask,
    Mod2Mask|LockMask,
    Mod2Mask|Mod5Mask,
    Mod2Mask|LockMask|Mod5Mask,
    LockMask,
    Mod5Mask,
    LockMask|Mod5Mask
};

static const int sAllXLockCombsCount=8;

unsigned int CX11Toolkit::GetXMods(EKeyMod pMods,EKeyLock pLocks)
{
    if(pMods&KEYMOD_ANY)
        return AnyModifier;

    unsigned int lRet=0;

    if(pMods&KEYMOD_SHIFT)      lRet|=ShiftMask;
    if(pMods&KEYMOD_CONTROL)    lRet|=ControlMask;
    if(pMods&KEYMOD_ALT)        lRet|=Mod1Mask;
    if(pMods&KEYMOD_ALTGR)      lRet|=0x2000;
    if(pMods&KEYMOD_WIN)        lRet|=Mod4Mask;
    if(pLocks&KEYLOCK_NUM)      lRet|=Mod2Mask;
    if(pLocks&KEYLOCK_CAPS)     lRet|=LockMask;
    if(pLocks&KEYLOCK_SCROLL)   lRet|=Mod5Mask;

    return lRet;
}

EKeyCode CX11Toolkit::GetKey(KeySym pKeySym)
{
    if(!mKeysInit) InitKeys();

    for(int i=1;i<KEY_COUNT;i++)
    {
        if(pKeySym==mKeyToKeysym[i])
            return (EKeyCode)i;
    }

    return KEY_NONE;
}

KeySym CX11Toolkit::GetKeySym(const TKey &pKey)
{
    if(!mKeysInit) InitKeys();

    return mKeyToKeysym[pKey.GetCode()];
}

unsigned int CX11Toolkit::GetKeyCode(const TKey &pKey)
{
    if(!mKeysInit) InitKeys();

    return XKeysymToKeycode(mDisplay->GetDisplay(),
                        mKeyToKeysym[pKey.GetCode()]);
}

bool CX11Toolkit::KeysEqual(const TKey &p1,const TKey &p2)
{
    return (GetKeyCode(p1)==GetKeyCode(p2));
}

////////////////////////////////////////////////////////////////////
//////////////////                                 /////////////////
///////////////////     Direct protocol access   ///////////////////
//////////////////                                //////////////////
////////////////////////////////////////////////////////////////////

long CX11Toolkit::GetStockCursor(CPointer::EStock pStock)
{
    long lID;

    switch(pStock)
    {
    case CPointer::STOCK_DEFAULT:
        lID=XC_left_ptr;
        break;
    case CPointer::STOCK_UP:
        lID=XC_center_ptr;
        break;
    case CPointer::STOCK_MOVE:
        lID=XC_fleur;
        break;
    case CPointer::STOCK_CROSS:
        lID=XC_plus;
        break;
    case CPointer::STOCK_TEXT:
        lID=XC_xterm;
        break;
    case CPointer::STOCK_PEN:
        lID=XC_pencil;
        break;
    case CPointer::STOCK_HAND:
        lID=XC_hand2;
        break;
    default:
        lID=XC_left_ptr;
    }
    
    return XCreateFontCursor(mDisplay->GetDisplay(),lID);
}

long CX11Toolkit::GetCursor(unsigned char *pShape,unsigned char *pMask,int pSize,
                    int pX,int pY)
{
    Pixmap lBitmap=XCreateBitmapFromData(mDisplay->GetDisplay(),
            DefaultRootWindow(mDisplay->GetDisplay()),
            (char*)pShape,pSize,pSize);
    Pixmap lMask=XCreateBitmapFromData(mDisplay->GetDisplay(),
            DefaultRootWindow(mDisplay->GetDisplay()),
            (char*)pMask,pSize,pSize);

    img::CColor lFore(0.0,0.0,0.0);
    img::CColor lBack(1.0,1.0,1.0);

    XColor lXFore=CX11Color::GetX11Color(lFore)->GetXColor();
    XColor lXBack=CX11Color::GetX11Color(lBack)->GetXColor();

    return XCreatePixmapCursor(mDisplay->GetDisplay(),lBitmap,lMask,
                &lXFore,&lXBack,pX,pY);
}

void CX11Toolkit::GrabKey(const TKey&,EKeyMod pMods,bool /*pNormal*/,CWindow*)
{
    //Display *lD=mDisplay->GetDisplay();

    //int lXMods=GetXMods(pMods);

    if(pMods&KEYMOD_ANY)
    {
        ///\todo
//        XGrabKey(lD,GetKeyCode(pKey),lXMods,
//                pWindow?pWindow->GetID():DefaultRootWindow(lD),
//                pNormal,GrabModeAsync,GrabModeAsync);
    }
    else
    {
        for(int i=0;i<sAllXLockCombsCount;i++)
        {
        ///\todo
//            XGrabKey(lD,GetKeyCode(pKey),lXMods|sAllXLockCombs[i],
//                pWindow?pWindow->GetID():DefaultRootWindow(lD),
//                pNormal,GrabModeAsync,GrabModeAsync);
        }
    }
}

void CX11Toolkit::UngrabKey(const TKey&,EKeyMod pMods,CWindow*)
{
    //Display *lD=mDisplay->GetDisplay();

    //int lXMods=GetXMods(pMods);

    if(pMods&KEYMOD_ANY)
    {
        ///\todo
//        XUngrabKey(lD,GetKeyCode(pKey),lXMods,
//                pWindow?pWindow->GetID():DefaultRootWindow(lD));
    }
    else
    {
        for(int i=0;i<sAllXLockCombsCount;i++)
        {
        ///\todo
//            XUngrabKey(lD,GetKeyCode(pKey),lXMods|sAllXLockCombs[i],
//                pWindow?pWindow->GetID():DefaultRootWindow(lD));
        }
    }
}

void CX11Toolkit::UngrabPointer()
{
    XUngrabPointer(mDisplay->GetDisplay(),CurrentTime);
//    mActiveGrab=nullptr;
}

Pixmap CX11Toolkit::CreatePixmap(const CSize_i &pSize,uint32_t pDepth,CWindow*)
{
    //TODO:support multiple screens
    Display *lD=mDisplay->GetDisplay();

    return XCreatePixmap(lD,DefaultRootWindow(lD),pSize.x,pSize.y,pDepth);
}

void CX11Toolkit::FreePixmap(Pixmap pID)
{
    XFreePixmap(mDisplay->GetDisplay(),pID);
}

void CX11Toolkit::Flush()
{
    XFlush(mDisplay->GetDisplay());
}

void CX11Toolkit::Synchronize()
{
    XSync(mDisplay->GetDisplay(),false);
}

void CX11Toolkit::QueryPointer(CPhysicalWindow *pWindow,CPoint_d &pInWindow,CPoint_d &pInScreen)
{
    Window lWindowID;
    
    if(pWindow)
        lWindowID=throwing_cast<CX11Window*>(pWindow)->GetX11ID();
    else
        lWindowID=mDisplay->GetDefaultRoot();
        

    Window lDummyWin,lChild;
    unsigned int lDummy;

    int pRootPosX,pRootPosY,pWinPosX,pWinPosY;
    XQueryPointer(mDisplay->GetDisplay(),lWindowID,&lDummyWin,&lChild,&pRootPosX,
                    &pRootPosY,&pWinPosX,&pWinPosY,&lDummy);
    pInWindow=CPoint_d(pWinPosX,pWinPosY);
    pInScreen=CPoint_d(pRootPosX,pRootPosY);
}

CPhysicalWindow *CX11Toolkit::GetNewPhysicalWindow(CWindow *pWindow)
{
    return new CX11Window(pWindow,mDisplay);
}

CClipboard *CX11Toolkit::GetClipboard(int pCB)
{
    if(!mClipboards[pCB]) mClipboards[pCB]=new CX11Clipboard(pCB);

    return mClipboards[pCB];
}

bool CX11Toolkit::SupportsBuolaWMExtensions()
{
    if(!mNETSupportedInit)
    {
        mBuolaWMExtensions=false;

        mDisplay->GetWindowProperty(GetRootID(),mDisplay->Atoms().mNetSupported,mNETSupported);

        for(int i=0;i<mNETSupported.size();i++)
        {
            if(mNETSupported[i]==mDisplay->Atoms().mBuolaWMExtensions)
                mBuolaWMExtensions=true;
        }

        mNETSupportedInit=true;
    }

    return mBuolaWMExtensions;
}

img::CGC *CX11Toolkit::GetOpenGLGC()
{
    return new gl::COpenGLGC(new CGLXContext(nWithoutWindow));
}

/////////////////////////////////////////////////////////
///////////////////// internal //////////////////////////
/////////////////////////////////////////////////////////

void CX11Toolkit::Start()
{
    StartDisplay();
    CToolkit::Start();
    mDisplay->StartIM();
    XInitThreads();
}

void CX11Toolkit::Finish()
{
    FinishDisplay();
}

////////////////////
// focus and grab
//

///\todo see when this was called and how it is necessary
// void CX11Toolkit::SetGrab(SGrabInfo *pInfo)
// {
//     if(!(pInfo->mMode&(GRAB_FAKE|GRAB_BUTTON)))
//     {
//         unsigned int lMask=ButtonPressMask|EnterWindowMask|ButtonReleaseMask|
//                         LeaveWindowMask|PointerMotionMask;
// 
//         int lOwner=(pInfo->mMode&GRAB_BUTTON)?False:True;
// 
//         int lSync=GrabModeAsync;
// 
//     ///\todo
// //        Window lGID=pInfo->mWindow->GetID();
// //        Window lCID=(pInfo->mMode&GRAB_CONFINE)?pInfo->mWindow->GetID():None;
//     Window lGID=0,lCID=0;
// 
//         int a;
//         if((a=XGrabPointer(mDisplay->GetDisplay(),lGID,lOwner,lMask,lSync,GrabModeAsync,
//                     lCID,None,CurrentTime))!=GrabSuccess)
//         {
//             throw XCollision(I18N("Can't grab pointer... already grabbed"));
//         }
//         mActiveGrab=pInfo;
//     }
// ///\todo this!!
// msg_info() << "should call onphysicalgrab!!\n";
// //    pInfo->mWindow->OnPhysicalGrab(true);
// }

// void CX11Toolkit::RemoveGrab(SGrabInfo *pInfo)
// {
//     if(mActiveGrab==pInfo)
//         UngrabPointer();
// 
// 
// ///\todo this!!
// msg_info() << "should call onphysicalgrab!!\n";
// //    pInfo->mWindow->OnPhysicalGrab(false);
// }

// void CX11Toolkit::SetGrabTo(CWindow *pWindow,EGrabMode pMode)
// {
//     SGrabInfo *lInfo;
// 
//     if(mGrabInfo&&(mGrabInfo->mMode&GRAB_BUTTON))
//     {
//         RemoveGrab(mGrabInfo);
//         lInfo=mGrabInfo;
//         mGrabInfo=lInfo->mPrevious;
//         delete lInfo;
//     }
// 
//     if(!(pMode&GRAB_BUTTON))
//     {
//         if(mGrabInfo&&(!(pMode&GRAB_REPLACE)))
//             throw XCollision(I18N("Can't grab pointer... already grabbed"));
//     }
// 
//     lInfo=new SGrabInfo;
//     lInfo->mMode=pMode;
//     lInfo->mWindow=pWindow;
//     lInfo->mPrevious=mGrabInfo;
// 
//     SetGrab(lInfo);
// 
//     mGrabInfo=lInfo;
// }
// 
// void CX11Toolkit::RemoveGrabFrom(CWindow *pWindow)
// {
//     if(!mGrabInfo) throw XInvalid(I18N("Can't remove grab... no grab"));
// 
//     SGrabInfo *lInfo;
//     SGrabInfo *lNext=nullptr;
// 
//     for(lInfo=mGrabInfo;lInfo;lInfo=lInfo->mPrevious)
//     {
//         if(lInfo->mWindow==pWindow)
//             break;
//         lNext=lInfo;
//     }
// 
//     if(!lInfo) throw XInvalid(I18N("Can't remove grab... the window doesn't have one"));
// 
//     if(lNext)
//         lNext->mPrevious=lInfo->mPrevious;
// 
//     RemoveGrab(lInfo);  //in case it was active, event if it wasn't the current one
// 
//     if(mGrabInfo==lInfo)        //This was the current grab. Change it
//     {
//         if(lInfo->mPrevious)
//         {
//             SetGrab(lInfo->mPrevious);
//             mGrabInfo=lInfo->mPrevious;
//         }
//         else
//         {
//             mGrabInfo=nullptr;
//         }
//     }
// 
//     delete lInfo;
// 
//     for(lInfo=mGrabInfo;lInfo;lInfo=lInfo->mPrevious)
//     {
//         if(lInfo->mWindow==pWindow)
//             break;
//     }
// 
// ///\todo this!!
// msg_info() << "should call onphysicalgrab!!\n";
// //    pWindow->OnPhysicalGrab((bool)lInfo);
// }
// 
// void CX11Toolkit::RemoveGrabOfType(EGrabMode pType)
// {
//     if(!mGrabInfo) throw XInvalid(I18N("Tried to remove grab and there's no grab"));
// 
//     SGrabInfo *lInfo;
//     SGrabInfo *lNext=nullptr;
// 
//     for(lInfo=mGrabInfo;lInfo;lInfo=lInfo->mPrevious)
//     {
//         if(lInfo->mMode==pType)
//             break;
//         lNext=lInfo;
//     }
// 
//     if(!lInfo) throw XInvalid(I18N("Can't remove grab... the window doesn't have one"));
// 
//     if(lNext)
//         lNext->mPrevious=lInfo->mPrevious;
// 
// ///\todo this!!
// msg_info() << "should call onphysicalgrab!!\n";
// //    lInfo->mWindow->OnPhysicalGrab(false);
// 
//     RemoveGrab(lInfo);
// 
//     if(mGrabInfo==lInfo)        //This was the current grab. Change it
//     {
//         if(lInfo->mPrevious)
//         {
//             SetGrab(lInfo->mPrevious);
//             mGrabInfo=lInfo->mPrevious;
//         }
//         else
//         {
//             mGrabInfo=nullptr;
//         }
//     }
// 
//     delete lInfo;
// }

///////////////////////////
// internal display
//

void CX11Toolkit::StartDisplay()
{
    mDisplay=CX11Display::AddDefaultDisplay();
    mDisplay->Open();

    mDispatcher=new CX11Dispatcher(mDisplay,this);

    XAutoRepeatOn(mDisplay->GetDisplay());

    InitShapeExtension();
    InitDamageExtension();

    if(!mSMDisabled)
    {
        mSession=new CX11Session(this);
        mSession->Start();
    }
}

void CX11Toolkit::FinishDisplay()
{
    mDisplay->Close();

    if(!mSMDisabled)
    {
        mSession->Finish();
    }
}

/////////////////////
// shape extension
//

void CX11Toolkit::InitShapeExtension()
{
    if(!XShapeQueryExtension(mDisplay->GetDisplay(), &mShapeEvent, &mDamageError))
    {
        fatal_error("Shape extension not present.. giving up \n");
    }
}

void CX11Toolkit::InitDamageExtension()
{
    if(!XDamageQueryExtension(mDisplay->GetDisplay(), &mDamageEvent, &mDamageError))
    {
        fatal_error("Damage extension not present.. giving up \n");
    }
}

img::CSurface *CX11Toolkit::GetNewPixmap(const CSize_i &pSize,int pDepth,unsigned char *pData,int pStride)
{
    CPixmap *lReturn=new CPixmap;
    if(pData)
        lReturn->Create(pSize,pDepth,pData,pStride);
    else
        lReturn->Create(pSize,pDepth,pData,pStride);
    return lReturn;
}

///\todo
//CSurface *CX11Toolkit::GetNewPixmap(Pixmap pPixmap,const CSize_i &pSize,
//                                                int pDepth)
//{
//    CPixmap *lReturn=new CPixmap;
//    lReturn->Create(pPixmap,pSize,pDepth);
//    return lReturn;
//}

int CX11Toolkit::GetDefaultDepth()
{
    return mDisplay->GetDepth();
}

void CX11Toolkit::DisableSM()
{
    mSMDisabled=true;
}

CX11Toolkit *get_x11_toolkit()
{
    static std::unique_ptr<CX11Toolkit> lX11Toolkit;

    if(!lX11Toolkit)
    {
        lX11Toolkit.reset(new CX11Toolkit);
        lX11Toolkit->Start();
    }

    return lX11Toolkit.get();
}

CX11Display *get_default_x11_display()
{
    return get_x11_toolkit()->mDisplay;
}

/*namespace x11*/ } /*namespace gui*/ } /*namespace buola*/ }

namespace buola { namespace gui {

CToolkit *get_toolkit()
{
    return x11::get_x11_toolkit();
}

/*namespace gui*/ } /*namespace buola*/ }
